package com.jokerku.mini.schedule.sharding.impl;

import com.jokerku.mini.schedule.sharding.ShardingStrategy;

import java.util.*;

/**
 * @Author: guzq
 * @CreateTime: 2023/06/20 14:53
 * @Description: 平均分配策略
 * 平均分配 eg:
 * 有 3 台机器 8 个分片, 则 1=[0,1,2] 2=[3,4,5] 3=[6,7]
 * 有 3 台机器 9 个分片, 则 1=[0,1,2] 2=[3,4,5] 3=[6,7,8]
 * 有 3 台机器 10 个分片, 则 1=[0,1,2,3] 2=[4,5,6] 3=[7,8,9]
 * 有 3 台机器 11 个分片, 则 1=[0,1,2,3] 2=[4,5,6,7] 3=[8,9,10]
 * @Version: 1.0
 */
public class AverageAllocationShardingStrategy implements ShardingStrategy {

    @Override
    public Map<String, List<Integer>> sharding(final List<String> ipList, final int shardingTotalCount) {
        if (ipList.isEmpty()) {
            return Collections.emptyMap();
        }

        Map<String, List<Integer>> shardingResult = shardingAliquot(ipList, shardingTotalCount);
        addAliquant(ipList, shardingTotalCount, shardingResult);

        return shardingResult;
    }

    /**
     * 整除部分分配
     *
     * @param ipList             ip 集合
     * @param shardingTotalCount 分片总数
     * @return 分片结果
     */
    private Map<String, List<Integer>> shardingAliquot(final List<String> ipList, final int shardingTotalCount) {
        Map<String, List<Integer>> result = new LinkedHashMap<>(ipList.size(), 1);
        int itemCountPerSharding = shardingTotalCount / ipList.size();
        int count = 0;
        for (String each : ipList) {
            List<Integer> shardingItems = new ArrayList<>(itemCountPerSharding + 1);
            for (int i = count * itemCountPerSharding; i < (count + 1) * itemCountPerSharding; i++) {
                shardingItems.add(i);
            }
            result.put(each, shardingItems);
            count++;
        }
        return result;
    }

    /**
     * 分配不能被整除的部分
     *
     * @param ipList             ip 集合
     * @param shardingTotalCount 分片总数
     * @param shardingResults    整除部分分配结果
     */
    private void addAliquant(final List<String> ipList, final int shardingTotalCount, final Map<String, List<Integer>> shardingResults) {
        int aliquant = shardingTotalCount % ipList.size();
        int count = 0;
        for (Map.Entry<String, List<Integer>> entry : shardingResults.entrySet()) {
            if (count < aliquant) {
                entry.getValue().add(shardingTotalCount / ipList.size() * ipList.size() + count);
            }
            count++;
        }
    }

    @Override
    public String getType() {
        return "AVERAGE_ALLOCATION";
    }
}
